/********************************************
 * 功能说明: Web端User基础业务控制器
 * 模块名称:
 * 系统名称:
 * 软件版权: Frank
 * 系统版本: 1.0.0.1
 * 开发人员:
 * 开发时间: 2019-12-18 18:41:08
 * 审核人员:
 * 相关文档:
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.spring.arch.uaa.web.controller;

import com.codahale.metrics.annotation.Timed;
import com.querydsl.core.types.Predicate;
import com.spring.arch.common.dto.result.PageResultDTO;
import com.spring.arch.common.dto.result.ResultDTO;
import com.spring.arch.common.exception.ExceptionHolder;
import com.spring.arch.common.validator.constraints.Mobile;
import com.spring.arch.uaa.errorcode.UaaErrorCode;
import com.spring.arch.uaa.web.converter.WebUserConverter;
import com.spring.arch.uaa.web.dto.WebUserDTO;
import com.spring.arch.common.dto.result.PageResultDTO;
import com.spring.arch.common.dto.result.ResultDTO;
import com.spring.arch.common.exception.ExceptionHolder;
import com.spring.arch.common.validator.constraints.Mobile;
import com.spring.arch.uaa.model.User;
import com.spring.arch.uaa.oauth2.service.OauthUserDetailsServiceImpl;
import com.spring.arch.uaa.repository.UserRepository;
import com.spring.arch.uaa.service.UserService;
import com.spring.arch.uaa.web.converter.WebUserConverter;
import com.spring.arch.uaa.web.dto.WebUserDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.validation.Valid;
import java.security.Principal;
import java.util.StringJoiner;
import java.util.UUID;

import static com.spring.arch.uaa.errorcode.UaaErrorCode.*;


/**
 * Web端User基础业务控制器
 * @author Frank
 * @version 1.0.0.1
 * @since 2019-12-18 18:41:08
 */
@Slf4j
@RestController
@RequestMapping(value = "/uaa/w/user", produces = MediaType.APPLICATION_JSON_VALUE)
@Api(tags = {"Web端User管理API" })
public class WebUserController {

    @Autowired
    private UserService userService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private OauthUserDetailsServiceImpl oauthUserDetailsService;
    @Autowired
    private WebUserConverter userConverter;

    /**
     * 新建操作
     *
     * @param userDTO 新建资源的DTO
     * @return 新建资源
     */
    @Timed
    @ApiOperation(value = "新建操作")
    @PreAuthorize("#oauth2.hasAnyScope('web') and hasRole('ROLE_000001')")
    @PostMapping
    public ResultDTO<WebUserDTO> create(@RequestBody @Valid final WebUserDTO userDTO) {
        final User model = this.userConverter.toModel(userDTO);
        this.userService.save(model);
        if (log.isInfoEnabled()) {
            log.info("{} instance {} was created.", User.class.getSimpleName(), model.getId());
        }
        return this.userConverter.toResultDTO(model);
    }

    /**
     * 删除操作
     *
     * @param id 实体对象ID
     * @return 操作结果
     */
    @Timed
    @ApiOperation(value = "删除操作")
    @PreAuthorize("#oauth2.hasAnyScope('web') and hasRole('ROLE_000001')")
    @DeleteMapping(value = "/{id}")
    public ResultDTO<Void> delete(@PathVariable final UUID id) {
        this.userService.delete(id);
        if (log.isInfoEnabled()) {
            log.info("{} instance {} was disable.", User.class.getSimpleName(), id);
        }
        return ResultDTO.success();
    }

    /**
     * 更新操作
     *
     * @param id 实体对象ID
     * @param userDTO 更新资源的DTO
     * @return 更新后资源
     */
    @Timed
    @ApiOperation(value = "更新操作")
    @PreAuthorize("#oauth2.hasAnyScope('web') and hasRole('ROLE_000001')")
    @PutMapping(value = "/{id}")
    public ResultDTO<WebUserDTO> update(@PathVariable final UUID id, @RequestBody @Valid final WebUserDTO userDTO) {
        userDTO.setId(id);
        final User model = this.userConverter.toModel(userDTO);
        this.userService.save(model);
        if (log.isInfoEnabled()) {
            log.info("{} instance {} was updated.", User.class.getSimpleName(), model.getId());
        }
        return this.userConverter.toResultDTO(model);
    }

    /**
     * 锁定|解锁操作
     *
     * @param id 实体对象id
     * @return 操作结果
     */
    @Timed
    @ApiOperation(value = "锁定|解锁操作")
    @PreAuthorize("#oauth2.hasAnyScope('web') and hasRole('ROLE_000001')")
    @PostMapping(value = "/lock/{id}")
    public ResultDTO lock(@PathVariable UUID id, @RequestParam String password, @ApiIgnore Principal currentUser) {
        // 当前用户密码Base64解密
        byte[] bytes = Base64Utils.decodeFromString(password);
        if (ArrayUtils.isEmpty(bytes)) {
            throw ExceptionHolder.serviceException(UaaErrorCode.E2001005005);
        }
        if (currentUser == null || StringUtils.isBlank(currentUser.getName())) {
            throw ExceptionHolder.serviceException(UaaErrorCode.E2001005006);
        }
        User user = oauthUserDetailsService.loadUserByMobileOrUserName(currentUser.getName());
        String salt = user.getSalt();
        String rawPassword = new StringJoiner("$").add(new String(bytes)).add(salt).toString();
        boolean matches = passwordEncoder.matches(rawPassword, user.getPassword());
        if (!matches) {
            throw ExceptionHolder.serviceException(UaaErrorCode.E2001005005);
        }
        this.userService.lock(id);
        if (log.isInfoEnabled()) {
            log.info("{} instance {} was locked.", User.class.getSimpleName(), id);
        }
        return ResultDTO.success();
    }

    /**
     * 用户密码重置
     *
     * @param mobile 手机号
     * @return 操作结果
     */
    @Timed
    @ApiOperation(value = "用户密码重置")
    @PreAuthorize("#oauth2.hasAnyScope('web') and hasRole('ROLE_000001')")
    @PostMapping(value = "/password/set/{mobile}")
    public ResultDTO setPassword(@PathVariable String mobile, @RequestParam String password) {
        userService.resetPassword(mobile, password);
        if (log.isInfoEnabled()) {
            log.info("{} instance {} was reset password.", User.class.getSimpleName(), mobile);
        }
        return ResultDTO.success();
    }

    /**
     * 修改手机号
     *
     * @param mobile 手机号
     * @return 操作结果
     */
    @Timed
    @ApiOperation(value = "修改手机号")
    @PreAuthorize("#oauth2.hasAnyScope('web') and hasRole('ROLE_000001')")
    @PostMapping(value = "/mobile/update/{mobile}/{newMobile}")
    public ResultDTO updateMobile(@PathVariable @Mobile String mobile, @PathVariable @Mobile String newMobile) {
        userService.resetMobile(mobile, newMobile);
        return ResultDTO.success();
    }

    /**
     * 获取分页数据
     *
     * @param pageable 分页+排序参数
     * @return 分页数据
     */
    @Timed
    @ApiOperation(value = "获取分页数据")
    @PostMapping(value = "/s")
    public PageResultDTO<WebUserDTO> search(@PageableDefault(sort = {"createdDate"}, direction = Sort.Direction.DESC) final Pageable pageable,
                                            @QuerydslPredicate(root = User.class) Predicate predicate) {
        final Page<User> models = this.userRepository.findAll(predicate, pageable);
        return this.userConverter.toResultDTO(models);
    }

    /**
     * 取得详细数据
     *
     * @param id 实体对象ID
     * @return 资源详细
     */
    @Timed
    @ApiOperation(value = "获取详细数据")
    @GetMapping(value = "/{id}")
    public ResultDTO<WebUserDTO> get(@PathVariable final UUID id) {
        final User model = this.userRepository.findById(id)
                    .orElseThrow(() -> ExceptionHolder.serviceException(UaaErrorCode.E2001005001, User.class.getSimpleName()));
        return this.userConverter.toResultDTO(model);
    }
}
